{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "95982bf1-7d9d-4dd6-a4ad-9de0719fe17f",
   "metadata": {},
   "source": [
    "# Parallel tool use\n",
    "\n",
    "In the [Chains with multiple tools](/docs/use_cases/tool_use/multiple_tools) guide we saw how to build function-calling chains that select between multiple tools. Some models, like the OpenAI models released in Fall 2023, also support parallel function calling, which allows you to invoke multiple functions (or the same function multiple times) in a single model call. Our previous chain from the multiple tools guides actually already supports this, we just need to use an OpenAI model capable of parallel function calling."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3fafec38-443a-42ad-a913-5be7667e3734",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "We'll need to install the following packages for this guide:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "78411bf1-0117-4f33-a3d7-f3d77a97bb78",
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install --upgrade --quiet langchain langchain-openai"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "59d08fd0-ddd9-4c74-bcea-a5ca3a86e542",
   "metadata": {},
   "source": [
    "And set these environment variables:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4185e74b-0500-4cad-ace0-bac37de466ac",
   "metadata": {},
   "outputs": [],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass()\n",
    "\n",
    "# If you'd like to use LangSmith, uncomment the below\n",
    "# os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
    "# os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d28159f5-b7d0-4385-aa44-4cd1b64507bb",
   "metadata": {},
   "source": [
    "## Tools"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "e13ec98c-8521-4d63-b521-caf92da87b70",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.tools import tool\n",
    "\n",
    "\n",
    "@tool\n",
    "def multiply(first_int: int, second_int: int) -> int:\n",
    "    \"\"\"Multiply two integers together.\"\"\"\n",
    "    return first_int * second_int\n",
    "\n",
    "\n",
    "@tool\n",
    "def add(first_int: int, second_int: int) -> int:\n",
    "    \"Add two integers.\"\n",
    "    return first_int + second_int\n",
    "\n",
    "\n",
    "@tool\n",
    "def exponentiate(base: int, exponent: int) -> int:\n",
    "    \"Exponentiate the base to the exponent power.\"\n",
    "    return base**exponent"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "119d419c-1c61-4e0d-834a-5dabb72f5514",
   "metadata": {},
   "source": [
    "# Chain\n",
    "\n",
    "Notice we use an `-1106` model, which as of this writing is the only kind that supports parallel function calling:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "c35359ae-a740-48c5-b5e7-1a377fb25aa2",
   "metadata": {},
   "outputs": [],
   "source": [
    "from operator import itemgetter\n",
    "from typing import Union\n",
    "\n",
    "from langchain.output_parsers import JsonOutputToolsParser\n",
    "from langchain_core.runnables import (\n",
    "    Runnable,\n",
    "    RunnableLambda,\n",
    "    RunnableMap,\n",
    "    RunnablePassthrough,\n",
    ")\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "model = ChatOpenAI(model=\"gpt-3.5-turbo-1106\")\n",
    "tools = [multiply, exponentiate, add]\n",
    "model_with_tools = model.bind_tools(tools)\n",
    "tool_map = {tool.name: tool for tool in tools}\n",
    "\n",
    "\n",
    "def call_tool(tool_invocation: dict) -> Union[str, Runnable]:\n",
    "    \"\"\"Function for dynamically constructing the end of the chain based on the model-selected tool.\"\"\"\n",
    "    tool = tool_map[tool_invocation[\"type\"]]\n",
    "    return RunnablePassthrough.assign(output=itemgetter(\"args\") | tool)\n",
    "\n",
    "\n",
    "# .map() allows us to apply a function to a list of inputs.\n",
    "call_tool_list = RunnableLambda(call_tool).map()\n",
    "chain = model_with_tools | JsonOutputToolsParser() | call_tool_list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "ea6dbb32-ec9b-4c70-a90f-a2db93978cf1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'type': 'multiply',\n",
       "  'args': {'first_int': 23, 'second_int': 7},\n",
       "  'output': 161},\n",
       " {'type': 'add', 'args': {'first_int': 5, 'second_int': 18}, 'output': 23},\n",
       " {'type': 'add',\n",
       "  'args': {'first_int': 1000000, 'second_int': 1000000000},\n",
       "  'output': 1001000000},\n",
       " {'type': 'exponentiate',\n",
       "  'args': {'base': 37, 'exponent': 3},\n",
       "  'output': 50653}]"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke(\n",
    "    \"What's 23 times 7, and what's five times 18 and add a million plus a billion and cube thirty-seven\"\n",
    ")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "poetry-venv",
   "language": "python",
   "name": "poetry-venv"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
